package foo.bar.helloworld;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class DictionaryParser extends DefaultHandler {
	/**
	 * Елемент словника
	 * @author nightmaresseller
	 */
	public class Term {
		/**
		 * Номер пари ім’я-значення
		 */
		private int position;
		/**
		 * Ім’я
		 */
		private String name;
		/**
		 * Значення
		 */
		private String value;
		private Term() {
			position = 0;
			name = "";
			value = "";
		}
		/**
		 * @return the name
		 */
		public String getName() {
			return name;
		}
		/**
		 * @param name the name to set
		 */
		public void setName(String name) {
			this.name = name;
		}
		/**
		 * @return the position
		 */
		public int getPosition() {
			return position;
		}
		/**
		 * @param position the position to set
		 */
		public void setPosition(int position) {
			this.position = position;
		}
		/**
		 * @return the value
		 */
		public String getValue() {
			return value;
		}
		/**
		 * @param value the value to set
		 */
		public void setValue(String value) {
			this.value = value;
		}
	}
	/**
	 * Список пар ім’я значення
	 */
	private ArrayList<Term> terms;
	/**
	 * Поточна пара
	 */
	private Term term;
	/**
	 * Показник, що парсер знаходиться всередині елемента <terms> - списку пар
	 */
	private boolean inList; 
	/**
	 * Показник, що парсер знаходиться всередині елемента <term> - пари ім’я значення
	 */
	private boolean inListItem;
	/**
	 * Показник, що парсер знаходиться всередині елемента  <position>
	 */
	private boolean inPosition;
	/**
	 * Шлях до XSLT таблиці стилів
	 */
	private static File xsltFile = new File("src/xml/ant2dict.xml");
	
	DictionaryParser() {
		terms = new ArrayList<Term>();
		inListItem = false;
		inList = false;
	}
	/**
	 * Отримання "словника"
	 * @return "словник"
	 */
	public Term[] getTerms() {
		Term retTerms[] = new Term[terms.size()];  
		for (int i = 0; i < retTerms.length; i++) {
			retTerms[i] = terms.get(i);
		}
		return retTerms;
	}
	/**
	 * Перетворення і парсинг XML документа
	 * @param xmlFileName шлях до файлу XML документа
	 * @throws TransformerException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 */
	public void parse(String xmlFileName) 
			throws TransformerException, ParserConfigurationException, 
				SAXException, IOException {
		File xmlFile = new File(xmlFileName);
		File transformedXml = new File(xmlFileName + "~");
		Source xmlSource = new StreamSource(xmlFile);
		Source xsltSource = new StreamSource(xsltFile);
		Result transResult = new StreamResult(transformedXml);
		
		/*
		 * Перетворення документа за допомогою XSLT
		 */
		// Створення фабрики XSLT-процесорів
		TransformerFactory transFactory = TransformerFactory.newInstance();
		// Створення процесора, що застосовує до документів задану таблицю стилів
		Transformer trans = transFactory.newTransformer(xsltSource);
		// Застосування таблиці стилів
		trans.transform(xmlSource, transResult);
		
		//Парсин перетвореного документа
		// Створення фабрики SAX-парсерів
		SAXParserFactory parserFactory = SAXParserFactory.newInstance();
		/*
		 * Встановлення параметрів парсера
		 */
		// Враховувати простори імен
		parserFactory.setNamespaceAware(true);
		// Перевіряти коректність документа
		parserFactory.setValidating(true);
		// Створення парсера 
		SAXParser parser = parserFactory.newSAXParser();
		// Парсинг документа
		parser.parse(transformedXml, this);
	}
	/**
	 * Метод що викликається при знаходженні парсером відкриваючого тега
	 */
	public void startElement(String uri, String localName, String qName, 
							 Attributes attributes) throws SAXException 
	{
		if (localName.equals("terms")) {
			inList = true;
		}
		if (inList && localName.equals("term")) {
			inListItem = true;
			term = new Term();
			if ((term.name = attributes.getValue("name")) == null) {
				System.out.println("Error: Term skipped due to its name don't specified");
				inListItem = false;
			}
			term.value = attributes.getValue("name");
		}
		if (inListItem && localName.equals("position")) {
			inPosition = true;
		}
	}
	/**
	 * Метод що викликається при знаходженні парсером простого тексту
	 */
	public void	characters(char[] ch, int start, int length) {
		if (inPosition) {
			String strVal = new String(ch, start, length);
			try {
				term.position = Integer.parseInt(strVal);
			} catch(Exception ex) {
				System.out.println("Error: " + strVal + " in \"position\" element is" 
						+ " not an integer value");
			}
		}
	}
	/**
	 * Метод що викликається при знаходженні парсером закриваючого тега
	 */
	public void	endElement(String uri, String localName, String qName) {
		if (localName.equals("terms")) {
			inList = false;
		}
		if (localName.equals("term")) {
			if (inListItem) {
				terms.add(term);
			}
			inListItem = false;
		}
		if (localName.equals("position")) {
			inPosition = false;
		}
	}
}
